package com.infolab.aims.common;

import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;

import com.infolab.aims.GlobalConstant;
import com.infolab.aims.common.bean.SearchCriteria;
import com.infolab.aims.common.bean.SearchResult;

public abstract class PaginationHelper<T> {

	private Class types;  
	
	public PaginationHelper() {

		types = ((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
	}
	
	public abstract String[] getValidColumnName();
	
	public abstract String[] getValidSortName();
	
	public abstract String getDefaultSortName();
	
	
	public SearchResult getSearchResult(EntityManager em,SearchCriteria searchCriteria)
	{
		CriteriaBuilder cb = em.getCriteriaBuilder();
		
		CriteriaQuery cq = cb.createQuery(types);
		
		CriteriaQuery<Long> cqCount = cb.createQuery(Long.class);
		Root<T> root = cq.from(types);
		List<Predicate> criteria = new ArrayList<Predicate>();
		String [] validSearchName = getValidColumnName();
		String [] validSortName = getValidSortName();
		for(int i=0;i<validSearchName.length;i++)
		{
			Object searchValue = searchCriteria.getSearchCriteria(validSearchName[i]);
			if(searchValue!=null)
			{
				
				if(searchValue instanceof Boolean){
					criteria.add(cb.equal(root.get(validSearchName[i]), (Boolean)searchValue));
				}
				else {
					Object obj = root.get(validSearchName[i]);
					criteria.add(cb.like(cb.upper(root.<String>get(validSearchName[i])), "%"+StringUtils.upperCase((String)searchValue)+"%"));
				}
			}
		}
		if (criteria.size() == 0) {
		} else if (criteria.size() == 1) {
		cq.where(criteria.get(0));
		cqCount.where(criteria.get(0));
		} else {
		cq.where(cb.and(criteria.toArray(new Predicate[0])));
		cqCount.where(cb.and(criteria.toArray(new Predicate[0])));
		}
	
		
		cq.select(root);
		
		if(StringUtils.isEmpty(searchCriteria.getSortName()))
		{
			cq.orderBy(cb.asc(root.get(getDefaultSortName())));
			searchCriteria.setSortName(getDefaultSortName());
			searchCriteria.setSortType(SearchCriteria.SORTTYPE_ASC);
		}
		else
		{
			if(ArrayUtils.contains(validSortName, searchCriteria.getSortName()))
			{
				if(searchCriteria.isAscendingSort())
					cq.orderBy(cb.asc(root.get(searchCriteria.getSortName())));
				else
					cq.orderBy(cb.desc(root.get(searchCriteria.getSortName())));
			}
		}
		Query q = em.createQuery(cq);
		int curPage = searchCriteria.getCurPage();
		int maxRow = 10;
		if(searchCriteria.getMaxRow()!=null)
			maxRow = searchCriteria.getMaxRow();
		int startPosition = (curPage-1)*maxRow;
		List<T> result = em.createQuery(cq).setMaxResults(maxRow).setFirstResult(startPosition).getResultList();
		
		root = (Root<T>) cqCount.from(types);
		
		cqCount.select(cb.count(root));
		int countRow = em.createQuery(cqCount).getSingleResult().intValue();
		int maxPage = (int) Math.ceil(countRow*1.0f/maxRow);
		
		SearchResult searchResult = new SearchResult();
		searchResult.setSearchCriteria(searchCriteria);
		searchResult.setResult(result);
		searchResult.setMaxPage(maxPage);
		searchResult.setResultCount(countRow);
		if(startPosition==0)
			searchResult.setRecordNo(1);
		else
			searchResult.setRecordNo(startPosition+1);
		
		return searchResult;
	}
	
	public SearchResult getSearchResultFromQueryHelper(EntityManager em,SearchCriteria searchCriteria)
	{
		String [] validSearchName = getValidColumnName();
		String [] validSortName = getValidSortName();
		if(getQueryHelper()==null)
			throw new RuntimeException("Query helper hasn't been overriden");
		
		QueryHelper queryHelper = getQueryHelper();
		
		for(int i=0;i<validSearchName.length;i++)
		{
			Object searchValue = searchCriteria.getSearchCriteria(validSearchName[i]);
			if(searchValue!=null)
			{
				if(StringUtils.contains(validSearchName[i],"accessDepartmentId"))
				{
					queryHelper.where("("+ validSearchName[i]+" is null or "+validSearchName[i]+"="+searchValue+")" );
				}
				else if(searchValue instanceof Boolean){
					boolean tmp = (Boolean) searchValue;
					queryHelper.where(validSearchName[i]+" ="+(tmp?"1":"0"));
				}
				else if(searchValue instanceof String)
				{
					String tmp = (String)searchValue;
					if(!StringUtils.isEmpty(tmp))
						queryHelper.where("upper("+validSearchName[i]+") like '%"+StringUtils.upperCase(tmp)+"%'");
				}
				else if(searchValue instanceof ParameterDateQueryHelper)
				{
					ParameterDateQueryHelper tmp = (ParameterDateQueryHelper)searchValue;
					if(tmp.getStartDate()!=null && tmp.getEndDate()!=null)
					{
						queryHelper.where("date_format("+validSearchName[i]+", '%Y-%m-%e') between '"+DateFormatUtils.format(tmp.getStartDate(),GlobalConstant.DATE_FORMAT_JAVA)+"' and '"+DateFormatUtils.format(tmp.getEndDate(),GlobalConstant.DATE_FORMAT_JAVA)+"'");
					}
					else if(tmp.getStartDate()!=null )
					{
						queryHelper.where("date_format("+validSearchName[i]+", '%Y-%m-%e') >= '"+DateFormatUtils.format(tmp.getStartDate(),GlobalConstant.DATE_FORMAT_JAVA)+"'");
					}
					else if(tmp.getEndDate()!=null )
					{
						queryHelper.where("date_format("+validSearchName[i]+", '%Y-%m-%e') <= '"+DateFormatUtils.format(tmp.getEndDate(),GlobalConstant.DATE_FORMAT_JAVA)+"'");
					}
				}
			}
		}
		
		if(StringUtils.isEmpty(searchCriteria.getSortName()))
		{
			queryHelper.order(getDefaultSortName()+" "+SearchCriteria.SORTTYPE_DESC);
			searchCriteria.setSortName(getDefaultSortName());
			searchCriteria.setSortType(SearchCriteria.SORTTYPE_DESC);
		}
		else
		{
			if(ArrayUtils.contains(validSortName, searchCriteria.getSortName()))
			{
				queryHelper.order(searchCriteria.getSortName()+" "+searchCriteria.getSortType());
			}
		}
		
		int curPage = searchCriteria.getCurPage();
		int maxRow = 10;
		if(searchCriteria.getMaxRow()!=null)
			maxRow = searchCriteria.getMaxRow();
		int startPosition = (curPage-1)*maxRow;
		List<T> result = em.createQuery(queryHelper.getResultSql()).setMaxResults(maxRow).setFirstResult(startPosition).getResultList();
		
		long tCountRow = em.createQuery(queryHelper.getCountSql(),Long.class).getSingleResult();
		int countRow = (int) tCountRow;
		int maxPage = (int) Math.ceil(countRow*1.0f/maxRow);
		
		SearchResult searchResult = new SearchResult();
		searchResult.setSearchCriteria(searchCriteria);
		searchResult.setResult(result);
		searchResult.setMaxPage(maxPage);
		searchResult.setResultCount(countRow);
		if(startPosition==0)
			searchResult.setRecordNo(1);
		else
			searchResult.setRecordNo(startPosition+1);
		
		return searchResult;
		
	}
	
	public QueryHelper getQueryHelper()
	{
		return null;	
	}
	
}
